<?php 
/**
 * This file is part of DomAr.
 * 
 * DomAr is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * DomAr is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with DomAr.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * @link http://inditel.ee/
 * @copyright 2007-2009 Inditel Meedia OÜ
 * @author Oliver Leisalu <oliver@inditel.ee>
 */

/**
 * 
 * Validation errors and codes:
 * <ul>
 * <li>51 - image resize failed. </li>
 * <li>52 - image cut failed. </li>
 * <li>53 - Failed to watermark image. </li>
 * </ul>
* @package DomAr
* @subpackage Plugin
*/
class DomArImage extends DomArFile {
	
	
	/**
	* Cut smaller image from center of current image.
	* @param Int $width
	* @param Int $height
	* @param Array $settings
	*/
	public function cut( $width, $height, array $settings = array() ) {
		if( strtolower($this->getExt()) == 'gif') $this->toPng(); 
		
		$result = self::imageCut( $this->getPath(), $width, $height, $settings );
		if( $result === false && $this->getParent() )
			$this->getParent()->registerFailedValidation(52, $this->getProperty()->name, '"Bad image"');
		return $result;
	}
	
	/**
	* Resize image
	* @param Int $width
	* @param Int $height
	* @param Boolean $inSize If resized image will be in given box or its shorter side will match width or height.
	*/
	public function resize( $width, $height, $inSize = true ) {
		if( strtolower($this->getExt()) == 'gif') $this->toPng(); 
		
		$result = self::imageResize( $this->getPath(), $width, $height, $inSize );
		if( $result === false && $this->getParent() )
			$this->getParent()->registerFailedValidation(51, $this->getProperty()->name, '"Bad image"');
		return $result;
	}
	
	/**
	* Watermarks image
	* @param String $watermarkPath Path to image that will be watermarked.
	* @param Array $settings
	* @return Bool
	*/
	public function watermark( $watermarkPath, array $settings = array() ) {
		$result = self::imageWatermark( $this->getPath(), $watermarkPath, $settings );
		if( $result === false && $this->getParent() )
			$this->getParent()->registerFailedValidation(53, $this->getProperty()->name, '"Bad image"');
		return $result;
	}
	
	/**
	* Converts image to PNG
	* @return Boolean
	*/
	public function toPng() {
		$this->convertTo('png', $quality );
	}
	/**
	* Converts image to GIF
	* @return Boolean
	*/
	public function toGif() {
		$this->convertTo('gif', $quality );
	}
	
	/**
	* Converts image to JPG
	* @param Int $quality
	* @return Boolean
	*/
	public function toJpg( $quality = 100 ) {
		$this->convertTo('jpg', $quality );
	}
	
	protected function convertTo( $type, $quality = 100 ) {
		$ext = strtolower($this->getExt());
		if( $ext != $type ) {
			if( $ext == 'png')
				$image = imagecreatefrompng( $this->getpath() ); 
			else if( $ext == 'gif')
				$image = imagecreatefromgif( $this->getpath() ); 
			else if( $ext == 'jpg' or $ext == 'jpeg' or $ext == 'pjpeg')
				$image = imagecreatefromjpeg( $this->getpath() ); 
			
			if( $type == 'png' )
				imagepng( $image, $this->getpath() );  
			else if( $type == 'gif' )
				imagegif( $image, $this->getpath() );  
			else if( $type == 'jpg' or $type == 'jpeg' or $type == 'pjpeg' )
				imagejpeg( $image, $this->getpath(), $quality );  
			
			$name = explode('.',$this->getRelativePath());
			array_pop($name);
			$name = implode('.',$name).'.'.$type;
			$this->rename( '/'.$name );
				
			$this->changed();
		}
		return true;
	}
	
	/**
	 * Returns image size. Array keys are "width" and "height"
	 * @return Array width,height
	 */
	public function getImageSize() {
		$size = getimagesize( $this->getPath() );
		return array('width' => $size[0], 'height' => $size[1]);
	}
	
	/**
	* Convert image to html
	* 
	* Possible params are:
	* style - add CSS style to item
	* class - add CSS class to item
	* alt - add alt description to img tag.
	* 
	* @param Array $args
	* @return String
	*/
	public function toHtml( array $args = array() ) {
		if( in_array(strtolower($this->getExt()),  $this->getSupportedImageExtensions() ) ) {
			$alt = ' alt="'.($args['alt'] ? $args['alt'] : $this->getName()).'" ';
			$class = ' class="'.$args['class'].'" ';
			$style = ' style="'.$args['style'].'" ';		
			
			$result = '<img border="0" src="'.$this->getUrl().'" ' . $alt . $class . $style .' />';
			
			if( $args['link'] ) 
				$result = '<a '.($args['target'] ? 'target="'.$args['target'].'"' : NULL ).' href="'.$args['link'].'">'.$result.'</a>';
			return $result;
		
		} else {
			return parent::toHtml();
		}
	}
	
	public function getSupportedImageExtensions() {
		return array('jpg','png','gif','jpeg','pjpeg');
	}
	
	//---------------------------------
	// STATICS
	//---------------------------------
	
	public static function imageCut( $path, $cutWidth, $cutHeight ) {
		
		$imageType = strtolower(array_pop(explode('.',$path)));
		$destfile = $path;
		
		$file_size = getimagesize( $path );

		if( $imageType=='jpg' or $imageType=='jpeg' or $imageType=='pjpeg')
			$img_src = @imagecreatefromjpeg( $path );
		else if($imageType=='png' or $imageType=='x-png')
			$img_src = @imagecreatefrompng( $path );
		else if($imageType=='gif')
			$img_src = @imagecreatefromgif( $path );
		else
			throw new RuntimeException ('Unknown image file type:'.$imageType);
	
		if( $img_src == false )
			return false;
		
		// calculate widths / heights of cutting
		if( $file_size[0] >= $cutWidth ) {
			$srcStartX = round( ($file_size[0]-$cutWidth)/2 );
			$dstStartX = 0;
			$resampleWidth = $cutWidth;
		} else {
			$srcStartX = 0;
			$dstStartX = round( ($cutWidth-$file_size[0])/2 );
			$resampleWidth = $file_size[0];
		}
		
		if( $file_size[1] >= $cutHeight ) {
			$srcStartY = round(($file_size[1]-$cutHeight)/2);
			$dstStartY = 0;
			$resampleHeight = $cutHeight;
		} else {
			$srcStartY = 0;
			$dstStartY = round(($cutHeight-$file_size[1])/2);
			$resampleHeight = $file_size[1];
		}
		// --------------------------------
		
		
		$dsfile = imagecreatetruecolor( $resampleWidth, $resampleHeight );
		
		
		// Handle transparent PNG
		if($imageType=='png' or $imageType=='x-png' ) {
			imagealphablending($dsfile, false); 
		    imagesavealpha($dsfile,true); 
		    $transparent = imagecolorallocatealpha($dsfile, 255, 255, 255, 127); 
		    imagefilledrectangle($dsfile, 0, 0,$cutWidth, $cutHeight, $transparent); 
		    imagecolortransparent  ( $dsfile, $transparent); 
		}
 		// --------------------------------
 		
		//debug($dstStartX.':'. $dstStartY.' | '. $srcStartX.':'. $srcStartY.' | '. $resampleWidth.':'. $resampleHeight);
		
		imagecopyresampled( $dsfile, $img_src, $dstStartX, $dstStartY, $srcStartX, $srcStartY, $resampleWidth, $resampleHeight, $resampleWidth, $resampleHeight );

		
		if($imageType=='jpg' or $imageType=='jpeg' or $imageType=='pjpeg')
			imagejpeg($dsfile ,$destfile,100); 
		else if($imageType=='png' or $imageType=='x-png')
			imagepng($dsfile ,$destfile); 
		else if($imageType=='gif')
			imagegif($dsfile ,$destfile); 
		
		return true;
	
	}
	
	/**
	 * @param String $path
	 * @param Int $forcedwidth
	 * @param Int $forcedheight
	 * @param Bool $mustBeInForcedWidthOrHeight
	 * @return Bool
	 */
	public static function imageResize( $path, $forcedwidth, $forcedheight, $mustBeInForcedWidthOrHeight=true) {
		try {
			$destfile = $path;
			$imageType = strtolower(array_pop(explode('.',$path)));
			
			$is = getimagesize( $path );
			if( (int)$is[0] <= $forcedwidth && (int)$is[1] <= $forcedheight) {
				$orientation = 2;
			} else {
				$orientation = 1;
				list($iw, $ih) = self::imageResizeInfo($is[0], $is[1], $forcedwidth,$forcedheight,$mustBeInForcedWidthOrHeight);
			}
		
			if ( $orientation == 1 ){
				
				if( $imageType =='jpg' or $imageType=='jpeg' or $imageType=='pjpeg') {
					$img_src = @imagecreatefromjpeg( $path );
				} else if($imageType=='png' or $imageType=='x-png') {
					$img_src = @imagecreatefrompng( $path );
				} else if($imageType=='gif') {
					$img_src = @imagecreatefromgif( $path );
				} else {
					throw new RuntimeException ('Unknown image file type:'.$imageType);
				}
				
				if( $img_src == false )
					return false;
				
				$dsfile = imagecreatetruecolor( $iw, $ih );
				
				// Handle transparent PNG
				if($imageType=='png' or $imageType=='x-png' ) {
					imagealphablending($dsfile, false); 
				    imagesavealpha($dsfile,true); 
				    $transparent = imagecolorallocatealpha($dsfile, 255, 255, 255, 127); 
				    imagefilledrectangle($dsfile, 0, 0,$iw, $ih, $transparent); 
				    imagecolortransparent  ( $dsfile, $transparent); 
				}
				
				imagecopyresampled( $dsfile, $img_src, 0, 0, 0, 0, $iw, $ih, $is[0], $is[1] );
				
				if($imageType=='jpg' or $imageType=='jpeg' or $imageType=='pjpeg') {
					imagejpeg($dsfile ,$destfile,100); 
				} else if($imageType=='png' or $imageType=='x-png') {
					imagepng($dsfile ,$destfile); 
				} else if($imageType=='gif') {
					imagegif($dsfile ,$destfile); 
				} else {
					throw new RuntimeException ('Unknown image file type:'.$imageType);
				}
			} else if ( $orientation == 2 ){
				copy( $path , $destfile );
			}
			return true;
		} catch( Exception $e ) {
			return false;
		}
		
	}
	
	/**
	 * @param String $path
	 * @param Int $forcedwidth
	 * @param Int $forcedheight
	 * @param Bool $mustBeInForcedWidthOrHeight
	 * @return Array [0=>width, 1=>height]
	 */
	public static function imageResizeInfo( $width, $height, $forcedwidth, $forcedheight, $mustBeInForcedWidthOrHeight = true) {
		
		$suurus[0] = $width/$forcedwidth;
		$suurus[1] = $height/$forcedheight;

		if($mustBeInForcedWidthOrHeight == true) {
			rsort($suurus);
		} else {
			sort($suurus);
		}

		if($suurus[0]>1) {
			$reso[0] = round($width/$suurus[0]);
			$reso[1] = round($height/$suurus[0]);
		} else if($suurus[1]>1) {
			$reso[0] = round($width/$suurus[1]);
			$reso[1] = round($height/$suurus[1]);
		} else {
			$reso[0] = $width;
			$reso[1] = $height;
		}

		return $reso;
	}
	
	/**
	* $settings:
	* alpha: How invisible watermark image should be.
	* position: {b,c,t}{r,c,l} where b- bottom, c- center, t-top, r-right, l-left. combine these like yx -> br -> bottom-right. default position is br
	* Sample: $image->imageWatermark( $source, $watermarkSource, array(position='br', alpha=20))
	*
	* @param String $imageSource
	* @param String $waterMarkSource
	* @param Array $settings
	* @return Bool
	*/
	public static function imageWatermark( $imageSource, $waterMarkSource, array $settings ) {
		
		if( is_file($imageSource) === false )
			return false;
		
			if( is_file($waterMarkSource) === false)
			return false;
		
		try {
			if( !isset($settings['alpha']) ) $settings['alpha'] = 20;
			
			$watermark = @imagecreatefrompng($waterMarkSource);  
			if( $watermark == false )
				return false;
			
			$watermark_width = imagesx($watermark);  
			$watermark_height = imagesy($watermark); 
			 
			$image = imagecreatetruecolor($watermark_width, $watermark_height);  
			
			$imageType = strtolower(array_pop(explode('.',$imageSource)));
			if( $imageType =='jpg' or $imageType=='jpeg' or $imageType=='pjpeg') {
				$image = @imagecreatefromjpeg( $imageSource );
			} else if($imageType=='png' or $imageType=='x-png') {
				$image = @imagecreatefrompng( $imageSource );
			} else if($imageType=='gif') {
				$image = @imagecreatefromgif( $imageSource );
			} else {
				throw new RuntimeException ('Unknown image file type:'.$imageType);
			}
			
			if( $image == false )
				return false;
			
			$size = getimagesize($imageSource); 
			$posy = substr($settings['position'], 0, 1);
			$posx = substr($settings['position'], 1, 1);
			
			switch( $posy ) {
				case 'b':
					$dest_y = $size[1] - $watermark_height; 
				break;
				case 'c':
					$dest_y = ceil($size[1]/2-$watermark_height/2);
				break;
				case 't':
					$dest_y = 0; 
				break;
				default:
					$dest_y = $size[1] - $watermark_height; 
				break;
			}
			
			switch( $posx ) {
				case 'r':
					$dest_x = $size[0] - $watermark_width; 
				break;
				case 'c':
					$dest_x = ceil($size[0]/2-$watermark_width/2);
				break;
				case 'l':
					$dest_x = 0;  
				break;
				default:
					$dest_x = $size[0] - $watermark_width; 
				break;
			}
			 
			imageantialias( $image, true);
			self::imagecopymerge_alpha($image, $watermark, $dest_x, $dest_y, 0, 0, $watermark_width, $watermark_height, $settings['alpha']);  
	
			imagejpeg($image,$imageSource,100); 
				
			imagedestroy($image);  
			imagedestroy($watermark); 
			
			return true;
		} catch( Exception $e ) {
			return false;
		}
		
	}
	
	/**
	 * Mimics the imagecopymerge function included with the GD library, except it supports alpha channel
	 */
	public static function imagecopymerge_alpha($dst_im, $src_im, $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h, $opacity){
		
        $w = imagesx($src_im);
        $h = imagesy($src_im);
        
        $cut = imagecreatetruecolor($src_w, $src_h);
        imagecopy($cut, $dst_im, 0, 0, $dst_x, $dst_y, $src_w, $src_h);
        $opacity = 100 - $opacity;
        
        imagecopy($cut, $src_im, 0, 0, $src_x, $src_y, $src_w, $src_h);
        imagecopymerge($dst_im, $cut, $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h, $opacity);
    } 
}

/**
* @link http://inditel.ee/
* @copyright 2007-2009 Inditel Meedia OÜ
* @author Oliver Leisalu <oliver@inditel.ee>
* @package DomAr
* @subpackage Plugin
*/
class DomArProperty_DomArImage extends DomArProperty_DomArFile {

	/**
	* @param Array allowed file extensions
	*/
	protected $allowedExtensions = array('jpg','gif','jpeg','png');

	
	
	public function load( DomArObject $object, &$propertyValue ) {
		$d = new DomArImage( $propertyValue, $this->allowedExtensions, $this->root, $this->url, array('beforeadd'=>$this->listeners['beforeadd'],'afteradd'=>$this->listeners['afteradd'])  );
		$d->changeParent( $object, $this );
		return $d;
	}
	
	public function validate( DomArObject $object, &$propertyValue, array &$customParams = array() ) {
		return (is_object($propertyValue) && get_class($propertyValue) == 'DomArImage' or is_string($propertyValue) or $propertyValue === NULL )? 0 : 1;
	}
}

?>